/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * http://www.gnu.org/copyleft/gpl.html
 */
package l1j.server.server.model;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import l1j.opqlo.Server.L1World.L1World;
import l1j.server.Config;
import l1j.server.Thread.GeneralThreadPool;
import l1j.server.server.ActionCodes;
import l1j.server.server.datatables.ItemTable;
import l1j.server.server.datatables.UBSpawnTable;
import l1j.server.server.model.Instance.L1ItemInstance;
import l1j.server.server.model.Instance.L1MonsterInstance;
import l1j.server.server.model.Instance.L1PcInstance;
import l1j.server.server.model.item.L1ItemId;
import l1j.server.server.serverpackets.S_ServerMessage;
import l1j.server.server.templates.L1Item;
import l1j.server.server.utils.IntRange;
import l1j.server.server.utils.Random;

// Referenced classes of package l1j.server.server.model:
// L1UltimateBattle

public class L1UltimateBattle {
	class UbThread implements Runnable {
		/**
		 * 競技開始までをカウントダウンする。
		 *
		 * @throws InterruptedException
		 */
		private void countDown() throws InterruptedException {
			// XXX - このIDは間違っている
			final int MSGID_COUNT = 637;
			final int MSGID_START = 632;

			for (int loop = 0; loop < BEFORE_MINUTE * 60 - 10; loop++) { //TODO 開始10秒前まで待つ
				Thread.sleep(1000);
				// removeRetiredMembers();
			}
			L1UltimateBattle.this.removeRetiredMembers();

			L1UltimateBattle.this.sendMessage(MSGID_COUNT, "10"); //TODO 10秒前

			Thread.sleep(5000);
			L1UltimateBattle.this.sendMessage(MSGID_COUNT, "5"); //TODO 5秒前

			Thread.sleep(1000);
			L1UltimateBattle.this.sendMessage(MSGID_COUNT, "4"); //TODO 4秒前

			Thread.sleep(1000);
			L1UltimateBattle.this.sendMessage(MSGID_COUNT, "3"); //TODO 3秒前

			Thread.sleep(1000);
			L1UltimateBattle.this.sendMessage(MSGID_COUNT, "2"); //TODO 2秒前

			Thread.sleep(1000);
			L1UltimateBattle.this.sendMessage(MSGID_COUNT, "1"); //TODO 1秒前

			Thread.sleep(1000);
			L1UltimateBattle.this.sendMessage(MSGID_START, "無限大戰 開始"); // スタート
			L1UltimateBattle.this.removeRetiredMembers();
		}

		/**
		 * スレッドプロシージャ。
		 */
		// @Override
		@Override
		public void run() {
			try {
				L1UltimateBattle.this.setActive(true);
				this.countDown();
				L1UltimateBattle.this.setNowUb(true);
				for (int round = 1; round <= 4; round++) {
					L1UltimateBattle.this.sendRoundMessage(round);

					final L1UbPattern pattern = UBSpawnTable.getInstance()
							.getPattern(L1UltimateBattle.this._ubId, L1UltimateBattle.this._pattern);

					final ArrayList<L1UbSpawn> spawnList = pattern
							.getSpawnList(round);

					for (final L1UbSpawn spawn : spawnList) {
						if (L1UltimateBattle.this.getMembersCount() > 0) {
							spawn.spawnAll();
						}

						Thread.sleep(spawn.getSpawnDelay() * 1000);
						// removeRetiredMembers();
					}

					if (L1UltimateBattle.this.getMembersCount() > 0) {
						L1UltimateBattle.this.spawnSupplies(round);
					}

					this.waitForNextRound(round);
				}

				for (final L1PcInstance pc : L1UltimateBattle.this.getMembersArray()) // コロシアム内に居るPCを外へ出す
				{
					final int rndx = Random.nextInt(4);
					final int rndy = Random.nextInt(4);
					final int locx = 33503 + rndx;
					final int locy = 32764 + rndy;
					final short mapid = 4;
					L1Teleport.teleport(pc, locx, locy, mapid, 5, true);
					L1UltimateBattle.this.removeMember(pc);
				}
				L1UltimateBattle.this.clearColosseum();
				L1UltimateBattle.this.setActive(false);
				L1UltimateBattle.this.setNowUb(false);
			} catch (final Exception e) {
				_log.log(Level.SEVERE, e.getLocalizedMessage(), e);
			}
		}

		/**
		 * 全てのモンスターが出現した後、次のラウンドが始まるまでの時間を待機する。
		 *
		 * @param curRound
		 *            現在のラウンド
		 * @throws InterruptedException
		 */
		private void waitForNextRound(final int curRound) throws InterruptedException {
			final int WAIT_TIME_TABLE[] = { 6, 6, 2, 18 };

			final int wait = WAIT_TIME_TABLE[curRound - 1];
			for (int i = 0; i < wait; i++) {
				Thread.sleep(10000);
				// removeRetiredMembers();
			}
			L1UltimateBattle.this.removeRetiredMembers();
		}
	}
	private static Calendar getRealTime() {
		final TimeZone _tz = TimeZone.getTimeZone(Config.TIME_ZONE);
		final Calendar cal = Calendar.getInstance(_tz);
		return cal;
	}
	private int _locX;
	private int _locY;
	private L1Location _location; //TODO 中心點
	private short _mapId;
	private int _locX1;
	private int _locY1;

	private int _locX2;
	private int _locY2;
	private int _ubId;
	private int _pattern;

	private boolean _isNowUb;
	private boolean _active; //TODO UB入場可能～競技終了までtrue
	private int _minLevel;

	private int _maxLevel;
	private int _maxPlayer;
	private boolean _enterRoyal;
	private boolean _enterKnight;
	private boolean _enterMage;
	private boolean _enterElf;
	private boolean _enterDarkelf;
	private boolean _enterDragonKnight;
	private boolean _enterIllusionist;
	private boolean _enterMale;
	private boolean _enterFemale;
	private boolean _usePot;

	private int _hpr;

	private int _mpr;
	private static int BEFORE_MINUTE = 5; //TODO 5分前から入場開始

	private final Set<Integer> _managers = new HashSet<Integer>();

	private final SortedSet<Integer> _ubTimes = new TreeSet<Integer>();

	private static final Logger _log = Logger.getLogger(L1UltimateBattle.class
			.getName());

	private static String intToTimeFormat(final int n) {
		return n / 100 + ":" + n % 100 / 10 + "" + n % 10;
	}

	private final ArrayList<L1PcInstance> _members = new ArrayList<L1PcInstance>();

	private String[] _ubInfo;

	/**
	 * コンストラクタ。
	 */
	public L1UltimateBattle() {
	}

	public void addManager(final int npcId) {
		this._managers.add(npcId);
	}

	/**
	 * プレイヤーを参加メンバーリストへ追加する。
	 *
	 * @param pc
	 *            新たに参加するプレイヤー
	 */
	public void addMember(final L1PcInstance pc) {
		if (!this._members.contains(pc)) {
			this._members.add(pc);
		}
	}

	public void addUbTime(final int time) {
		this._ubTimes.add(time);
	}

	/**
	 * UBに参加可能か、レベル、クラスをチェックする。
	 *
	 * @param pc
	 *            UBに参加できるかチェックするPC
	 * @return 参加出来る場合はtrue,出来ない場合はfalse
	 */
	public boolean canPcEnter(final L1PcInstance pc) {
		_log.log(Level.FINE, "pcname=" + pc.getName() + " ubid=" + this._ubId
				+ " minlvl=" + this._minLevel + " maxlvl=" + this._maxLevel);
		// 参加可能なレベルか
		if (!IntRange.includes(pc.getLevel(), this._minLevel, this._maxLevel)) {
			return false;
		}

		// 参加可能なクラスか
		if (!(pc.isCrown() && this._enterRoyal || pc.isKnight() && this._enterKnight
				|| pc.isWizard() && this._enterMage || pc.isElf() && this._enterElf
				|| pc.isDarkelf() && this._enterDarkelf
				|| pc.isDragonKnight() && this._enterDragonKnight || pc
				.isIllusionist() && this._enterIllusionist)) {
			return false;
		}

		return true;
	}

	public boolean canUsePot() {
		return this._usePot;
	}

	public boolean checkUbTime() {
		final SimpleDateFormat sdf = new SimpleDateFormat("HHmm");
		final Calendar realTime = getRealTime();
		realTime.add(Calendar.MINUTE, BEFORE_MINUTE);
		final int nowTime = Integer.valueOf(sdf.format(realTime.getTime()));
		return this._ubTimes.contains(nowTime);
	}

	/**
	 * コロシアム上のアイテムとモンスターを全て削除する。
	 */
	private void clearColosseum() {
		for (final Object obj : L1World.getInstance().getVisibleObjects(this._mapId)
				.values()) {
			if (obj instanceof L1MonsterInstance) //TODO モンスター削除
			{
				final L1MonsterInstance mob = (L1MonsterInstance) obj;
				if (!mob.isDead()) {
					mob.setDead(true);
					mob.setStatus(ActionCodes.ACTION_Die);
					mob.setCurrentHpDirect(0);
					mob.deleteMe();

				}
			} else if (obj instanceof L1Inventory) //TODO アイテム削除
			{
				final L1Inventory inventory = (L1Inventory) obj;
				inventory.clearItems();
			}
		}
	}

	/**
	 * 参加メンバーリストをクリアする。
	 */
	public void clearMembers() {
		this._members.clear();
	}

	public boolean containsManager(final int npcId) {
		return this._managers.contains(npcId);
	}

	public int getHpr() {
		return this._hpr;
	}

	public L1Location getLocation() {
		return this._location;
	}

	public int getLocX1() {
		return this._locX1;
	}

	public int getLocX2() {
		return this._locX2;
	}

	public int getLocY1() {
		return this._locY1;
	}

	public int getLocY2() {
		return this._locY2;
	}

	public short getMapId() {
		return this._mapId;
	}

	public int getMaxLevel() {
		return this._maxLevel;
	}

	public int getMaxPlayer() {
		return this._maxPlayer;
	}

	/**
	 * 参加メンバーの配列を作成し、返す。
	 *
	 * @return 参加メンバーの配列
	 */
	public L1PcInstance[] getMembersArray() {
		return this._members.toArray(new L1PcInstance[this._members.size()]);
	}

	/**
	 * 参加メンバー数を返す。
	 *
	 * @return 参加メンバー数
	 */
	public int getMembersCount() {
		return this._members.size();
	}

	public int getMinLevel() {
		return this._minLevel;
	}

	public int getMpr() {
		return this._mpr;
	}

	public String getNextUbTime() {
		return intToTimeFormat(this.nextUbTime());
	}

	public int getUbId() {
		return this._ubId;
	}

	/**
	 * @return UB入場可能～競技終了まではtrue,それ以外はfalseを返す。
	 */
	public boolean isActive() {
		return this._active;
	}

	/**
	 * プレイヤーが、参加メンバーかを返す。
	 *
	 * @param pc
	 *            調べるプレイヤー
	 * @return 参加メンバーであればtrue、そうでなければfalse。
	 */
	public boolean isMember(final L1PcInstance pc) {
		return this._members.contains(pc);
	}

	/**
	 * UB中かを返す。
	 *
	 * @return UB中であればtrue、そうでなければfalse。
	 */
	public boolean isNowUb() {
		return this._isNowUb;
	}

	public String[] makeUbInfoStrings() {
		if (this._ubInfo != null) {
			return this._ubInfo;
		}
		final String nextUbTime = this.getNextUbTime();
		// クラス
		final StringBuilder classesBuff = new StringBuilder();
		if (this._enterDarkelf) {
			classesBuff.append("黑暗妖精 ");
		}
		if (this._enterMage) {
			classesBuff.append("法師 ");
		}
		if (this._enterElf) {
			classesBuff.append("妖精 ");
		}
		if (this._enterKnight) {
			classesBuff.append("騎士 ");
		}
		if (this._enterRoyal) {
			classesBuff.append("王族 ");
		}
		if (this._enterDragonKnight) {
			classesBuff.append("龍騎士 ");
		}
		if (this._enterIllusionist) {
			classesBuff.append("幻術師 ");
		}
		final String classes = classesBuff.toString().trim();
		// 性別
		final StringBuilder sexBuff = new StringBuilder();
		if (this._enterMale) {
			sexBuff.append("男 ");
		}
		if (this._enterFemale) {
			sexBuff.append("女 ");
		}
		final String sex = sexBuff.toString().trim();
		final String loLevel = String.valueOf(this._minLevel);
		final String hiLevel = String.valueOf(this._maxLevel);
		final String teleport = this._location.getMap().isEscapable() ? "可以" : "不可以";
		final String res = this._location.getMap().isUseResurrection() ? "可以" : "不可以";
		final String pot = "可以";
		final String hpr = String.valueOf(this._hpr);
		final String mpr = String.valueOf(this._mpr);
		final String summon = this._location.getMap().isTakePets() ? "可以" : "不可以";
		final String summon2 = this._location.getMap().isRecallPets() ? "可以" : "不可以";
		this._ubInfo = new String[] { nextUbTime, classes, sex, loLevel, hiLevel,
				teleport, res, pot, hpr, mpr, summon, summon2 };
		return this._ubInfo;
	}

	private int nextUbTime() {
		final SimpleDateFormat sdf = new SimpleDateFormat("HHmm");
		final int nowTime = Integer.valueOf(sdf.format(getRealTime().getTime()));
		SortedSet<Integer> tailSet = this._ubTimes.tailSet(nowTime);
		if (tailSet.isEmpty()) {
			tailSet = this._ubTimes;
		}
		return tailSet.first();
	}

	/**
	 * プレイヤーを参加メンバーリストから削除する。
	 *
	 * @param pc
	 *            削除するプレイヤー
	 */
	public void removeMember(final L1PcInstance pc) {
		this._members.remove(pc);
	}

	/**
	 * コロシアムから出たメンバーをメンバーリストから削除する。
	 */
	private void removeRetiredMembers() {
		final L1PcInstance[] temp = this.getMembersArray();
		for (final L1PcInstance element : temp) {
			if (element.getMapId() != this._mapId) {
				this.removeMember(element);
			}
		}
	}

	// setされたlocx1～locy2から中心点を求める。
	public void resetLoc() {
		this._locX = (this._locX2 + this._locX1) / 2;
		this._locY = (this._locY2 + this._locY1) / 2;
		this._location = new L1Location(this._locX, this._locY, this._mapId);
	}

	/**
	 * UBに参加しているプレイヤーへメッセージ(S_ServerMessage)を送信する。
	 *
	 * @param type
	 *            メッセージタイプ
	 * @param msg
	 *            送信するメッセージ
	 */
	private void sendMessage(final int type, final String msg) {
		for (final L1PcInstance pc : this.getMembersArray()) {
			pc.sendPackets(new S_ServerMessage(type, msg));
		}
	}

	/**
	 * ラウンド開始時のメッセージを送信する。
	 *
	 * @param curRound
	 *            開始するラウンド
	 */
	private void sendRoundMessage(final int curRound) {
		// XXX - このIDは間違っている
		final int MSGID_ROUND_TABLE[] = { 893, 894, 895, 896 };

		this.sendMessage(MSGID_ROUND_TABLE[curRound - 1], "");
	}

	private void setActive(final boolean f) {
		this._active = f;
	}

	public void setEnterDarkelf(final boolean enterDarkelf) {
		this._enterDarkelf = enterDarkelf;
	}

	public void setEnterDragonKnight(final boolean enterDragonKnight) {
		this._enterDragonKnight = enterDragonKnight;
	}

	public void setEnterElf(final boolean enterElf) {
		this._enterElf = enterElf;
	}

	public void setEnterFemale(final boolean enterFemale) {
		this._enterFemale = enterFemale;
	}

	public void setEnterIllusionist(final boolean enterIllusionist) {
		this._enterIllusionist = enterIllusionist;
	}

	public void setEnterKnight(final boolean enterKnight) {
		this._enterKnight = enterKnight;
	}

	public void setEnterMage(final boolean enterMage) {
		this._enterMage = enterMage;
	}

	public void setEnterMale(final boolean enterMale) {
		this._enterMale = enterMale;
	}

	public void setEnterRoyal(final boolean enterRoyal) {
		this._enterRoyal = enterRoyal;
	}

	public void setHpr(final int hpr) {
		this._hpr = hpr;
	}

	public void setLocX1(final int locX1) {
		this._locX1 = locX1;
	}

	public void setLocX2(final int locX2) {
		this._locX2 = locX2;
	}

	public void setLocY1(final int locY1) {
		this._locY1 = locY1;
	}

	public void setLocY2(final int locY2) {
		this._locY2 = locY2;
	}

	public void setMapId(final short mapId) {
		this._mapId = mapId;
	}

	public void setMaxLevel(final int level) {
		this._maxLevel = level;
	}

	public void setMaxPlayer(final int count) {
		this._maxPlayer = count;
	}

	public void setMinLevel(final int level) {
		this._minLevel = level;
	}

	public void setMpr(final int mpr) {
		this._mpr = mpr;
	}

	/**
	 * UB中かを設定する。
	 *
	 * @param i
	 *            true/false
	 */
	private void setNowUb(final boolean i) {
		this._isNowUb = i;
	}

	public void setUbId(final int id) {
		this._ubId = id;
	}

	public void setUsePot(final boolean usePot) {
		this._usePot = usePot;
	}

	/**
	 * コロシアム上へアイテムを出現させる。
	 *
	 * @param itemId
	 *            出現させるアイテムのアイテムID
	 * @param stackCount
	 *            アイテムのスタック数
	 * @param count
	 *            出現させる数
	 */
	private void spawnGroundItem(final int itemId, final int stackCount, final int count) {
		final L1Item temp = ItemTable.getInstance().getTemplate(itemId);
		if (temp == null) {
			return;
		}

		for (int i = 0; i < count; i++) {
			final L1Location loc = this._location.randomLocation(
					(this.getLocX2() - this.getLocX1()) / 2, false);
			if (temp.isStackable()) {
				final L1ItemInstance item = ItemTable.getInstance()
						.createItem(itemId);
				item.setEnchantLevel(0);
				item.setCount(stackCount);
				final L1GroundInventory ground = L1World.getInstance().getInventory(
						loc.getX(), loc.getY(), this._mapId);
				if (ground.checkAddItem(item, stackCount) == L1Inventory.OK) {
					ground.storeItem(item);
				}
			} else {
				L1ItemInstance item = null;
				for (int createCount = 0; createCount < stackCount; createCount++) {
					item = ItemTable.getInstance().createItem(itemId);
					item.setEnchantLevel(0);
					final L1GroundInventory ground = L1World.getInstance()
							.getInventory(loc.getX(), loc.getY(), this._mapId);
					if (ground.checkAddItem(item, stackCount) == L1Inventory.OK) {
						ground.storeItem(item);
					}
				}
			}
		}
	}

	/**
	 * ポーション等の補給アイテムを出現させる。
	 *
	 * @param curRound
	 *            現在のラウンド
	 */
	private void spawnSupplies(final int curRound) {
		if (curRound == 1) {
			this.spawnGroundItem(L1ItemId.ADENA, 1000, 60);
			this.spawnGroundItem(L1ItemId.POTION_OF_CURE_POISON, 3, 20);
			this.spawnGroundItem(L1ItemId.POTION_OF_EXTRA_HEALING, 5, 20);
			this.spawnGroundItem(L1ItemId.POTION_OF_GREATER_HEALING, 3, 20);
			this.spawnGroundItem(40317, 1, 5); //TODO 砥石
			this.spawnGroundItem(40079, 1, 20); //TODO 帰還スク
		} else if (curRound == 2) {
			this.spawnGroundItem(L1ItemId.ADENA, 5000, 50);
			this.spawnGroundItem(L1ItemId.POTION_OF_CURE_POISON, 5, 20);
			this.spawnGroundItem(L1ItemId.POTION_OF_EXTRA_HEALING, 10, 20);
			this.spawnGroundItem(L1ItemId.POTION_OF_GREATER_HEALING, 5, 20);
			this.spawnGroundItem(40317, 1, 7); //TODO 砥石
			this.spawnGroundItem(40093, 1, 10); //TODO ブランクスク(Lv4)
			this.spawnGroundItem(40079, 1, 5); //TODO 帰還スク
		} else if (curRound == 3) {
			this.spawnGroundItem(L1ItemId.ADENA, 10000, 30);
			this.spawnGroundItem(L1ItemId.POTION_OF_CURE_POISON, 7, 20);
			this.spawnGroundItem(L1ItemId.POTION_OF_EXTRA_HEALING, 20, 20);
			this.spawnGroundItem(L1ItemId.POTION_OF_GREATER_HEALING, 10, 20);
			this.spawnGroundItem(40317, 1, 10); //TODO 砥石
			this.spawnGroundItem(40094, 1, 10); //TODO ブランクスク(Lv5)
		}
	}

	/**
	 * アルティメットバトルを開始する。
	 *
	 * @param ubId
	 *            開始するアルティメットバトルのID
	 */
	public void start() {
		final int patternsMax = UBSpawnTable.getInstance().getMaxPattern(this._ubId);
		this._pattern = Random.nextInt(patternsMax) + 1; // 出現パターンを決める

		final UbThread ub = new UbThread();
		GeneralThreadPool.getInstance().execute(ub);
	}
}
